package expression;

import NextClass.NativeClass;
import NextClass.NextClassAPI;
import vm.baseNativeUseFunction.NativeUseFunction;
import vm.runtime.NextVMRuntime;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;

public class StringExpression implements Serializable {

    public static String FormatString(String exp) {
        exp = exp.replace("\\n","\n");
        exp = exp.replace("\\t","\t");
        exp = exp.replace("\\\"" , "\"");
        return exp;
    }

    /**
     *
     * @param expression
     * expression参数输入的是没有经过处理的表达式，例如 "hello world"
     * @param nextVMRuntime
     * 输入所在的运行环境
     * @return
     * @throws Exception
     */
    public static String getExpression
            (
            String expression,
            NextVMRuntime nextVMRuntime)
    throws Exception
    {
        /*
                String[] tokens = expression.split(
                ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)(?=([^(]*\\([^)]*\\))*[^)]*$)"
        );
         */
        String[] tokens = StringExpression.splitStringTokens(expression);
        StringBuffer stringBuffer = new StringBuffer();
        //System.out.println(nextVMRuntime.values);
        for (String string : tokens) {
            string = string.trim();
            Object Values = getValueFromNextVM(string , nextVMRuntime);
            if (string.isEmpty()) {
                continue;
            }
            /**
             *
             */
            else if (string.startsWith("\"") && string.endsWith("\"")) {
                try {
                    String ss = string.substring(1,string.length()-1);
                    ss = FormatString(ss);
                    stringBuffer.append(ss);
                    continue;
                }
                catch (Exception exception) {
                    throw new Exception("string error.");
                }
            }
            else if (IsNumber(string)) {
                stringBuffer.append(string);
            }
            if (NativeUseFunction.IsNativeUseFunctionCode(string)) {
                stringBuffer.append(NativeUseFunction.run_code(string,nextVMRuntime));
                continue;
            }
            NativeClass nativeClass = NextClassAPI.IsNativeClassInvokeCode(string , nextVMRuntime);
            if (nativeClass != null) {
                String Name = string.substring(0 , string.indexOf("(")).trim();
                String Invoke = Name.substring(Name.indexOf(".")+1).trim();
                String Content = string.substring(string.indexOf("(")+1,string.lastIndexOf(")")).trim();
                //String regex = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)(?=([^\\(]*\\([^\\)]*\\))*[^\\)]*$)";
                String[] split = StringExpression.splitStringTokens(Content);
                Object[] objects = BaseExpression.Deal(split,nextVMRuntime);

                stringBuffer.append( nativeClass.run(Invoke,nextVMRuntime,objects));
                continue;
            }
            NextVMRuntime UseFunction = MathExpression.CodeFunctionFromRuntime(string, nextVMRuntime);
            if (UseFunction != null) {
                UseFunction.run();
                stringBuffer.append(UseFunction.ReturnContent);
                continue;
            }
            else if (Values != null) {
                stringBuffer.append(Values);
                continue;
            } else if (isList(string , nextVMRuntime , stringBuffer)) {
                continue;
            }
            else {
                throw new Exception("Unknown Expression: "+string+" in \n"+expression);
            }
        }
        return stringBuffer.toString();
    }
    public static boolean IsNumber(String string) {
        try {
            double b = Double.parseDouble(string);
            return true;
        }catch (Exception e) {
            return false;
        }
    }
    public static Object getValueFromNextVM(String valueName, NextVMRuntime nextVMRuntime) {
        Object obj_1 = nextVMRuntime.values.get(valueName);
        if (obj_1 != null) {
            return obj_1;
        }
        else if (nextVMRuntime.LastRuntime != null) {
            return getValueFromNextVM(valueName , nextVMRuntime.LastRuntime);
        } else {
            return null;
        }
    }
    private static boolean isList(String code , NextVMRuntime nextVMRuntime,StringBuffer stringBuffer) throws Exception {
        try {
            String value = code.substring(0 , code.indexOf("[")).trim();
            String input = code.substring(code.indexOf("[")+1 , code.lastIndexOf("]")).trim();
            Object object = getValueFromNextVM(value , nextVMRuntime);
            if (object != null) {
                int index = (int) MathExpression.evaluateExpression(input , nextVMRuntime);
                ArrayList<Object> arrayList = (ArrayList<Object>) object;
                stringBuffer.append(arrayList.get(index));
            } else {
                throw new Exception("Unknown Value: "+value);
            }
            return true;
        }catch (Exception exception) {
            return false;
        }
    }
    public static Object GetListValue(String code , NextVMRuntime nextVMRuntime) throws Exception {
        try {
            String value = code.substring(0 , code.indexOf("[")).trim();
            String input = code.substring(code.indexOf("[")+1 , code.lastIndexOf("]")).trim();
            Object object = getValueFromNextVM(value , nextVMRuntime);
            if (object != null) {
                int index = (int) MathExpression.evaluateExpression(input , nextVMRuntime);
                ArrayList<Object> arrayList = (ArrayList<Object>) object;
                return arrayList.get(index);
            } else {
                throw new Exception("Unknown Value: "+value);
            }
        }catch (Exception exception) {
            return null;
        }
    }
    public static String[] splitStringTokens(String exp) {
        /**
         * "hello world"
         * "hello \"world"
         * "hello" , "world"
         * a("hello world" , "hello world" , b("a")),
         * "hello" , 100.0 , 200
         */
        boolean isIn_1 = false; // ( or )
        boolean isIn_2 = false; // "
        boolean isIn_3 = false; // , and ,
        int In_1_format_number = 0;
        ArrayList<String> TokenArrays = new ArrayList<>();

        StringBuffer tmp_str = new StringBuffer();
        String[] s_str = exp.split("");
        for (int i = 0; i < s_str.length ;i++) {
            /**
             * 先统计逗号进行分割
              */
            String s = s_str[i];
            if (s.equals("\"") && isIn_1) {
                tmp_str.append(s);
                continue;
            }
            else if (s.equals("\"") && !isIn_2) {
                isIn_2 = true;
                tmp_str.append(s);
                continue;
            }
            else if (s.equals("\"") && isIn_2) {
                isIn_2 = false;
                tmp_str.append(s);
                TokenArrays.add(tmp_str.toString());
                tmp_str = new StringBuffer();
                continue;
            }
            else if (s.equals("\\") && isIn_2) {
                i += 1;
                tmp_str.append(s);
                tmp_str.append(s_str[i]);
                continue;
            }
            else if (s.equals(",") && !isIn_1 && !isIn_2) {
                if (tmp_str.toString().trim().isEmpty()) {
                    continue;
                }
                TokenArrays.add(tmp_str.toString());
                tmp_str = new StringBuffer();
                continue;
            }
            else if (s.equals("(") && !isIn_2) {
                isIn_1 = true;
                In_1_format_number += 1;
                tmp_str.append(s);
                continue;
            }
            else if (s.equals(")") && isIn_1 && !isIn_2) {
                In_1_format_number -= 1;
                tmp_str.append(s);
                if (In_1_format_number == 0) {
                    TokenArrays.add(tmp_str.toString());
                    tmp_str = new StringBuffer();
                    isIn_1 = false;
                }
                continue;
            }
            else  {
                tmp_str.append(s);
                //System.out.println(i+1 + " "+s_str.length);
                if (i + 1 == s_str.length) {
                    TokenArrays.add(tmp_str.toString());
                }
            }
        }
        //System.out.println("===============================");
        //System.out.println(TokenArrays);
        //System.out.println(TokenArrays.size());
        //System.out.println(exp);
        //System.out.println("===============================");
        //System.out.println();
        return TokenArrays.toArray(new String[0]);
    }
}
